home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Mac Magazin/MacEasy 11
/
Mac Magazin and MacEasy Magazine CD - Issue 11.iso
/
Sharewarebibliothek
/
Entwickler
/
WASTE 1.1b1 Distribution
/
WASTE Source
/
WELineLayout.p
< prev
next >
Wrap
Text File
|
1995-06-01
|
19KB
|
659 lines
unit WELineLayout;
{ WASTE PROJECT: }
{ Line Layout, Getting and Setting Variables, etc. }
{ Copyright © 1993-1994 Marco Piovanelli }
{ All Rights Reserved }
interface
uses
WEDrawing;
procedure WEStopInlineSession (hWE: WEHandle);
function _WERemoveLine (lineIndex: LongInt;
pWE: WEPtr): OSErr;
function _WERecalBreaks (var startLine, endLine: LongInt;
hWE: WEHandle): OSErr;
procedure _WERecalSlops (firstLine, lastLine: LongInt;
hWE: WEHandle);
function WEGetTextLength (hWE: WEHandle): LongInt;
function WECountLines (hWE: WEHandle): LongInt;
function WEGetHeight (startLine, endLine: LongInt;
hWE: WEHandle): LongInt;
procedure WEGetSelection (var selStart, selEnd: LongInt;
hWE: WEHandle);
procedure WEGetDestRect (var destRect: LongRect;
hWE: WEHandle);
procedure WESetDestRect (var destRect: LongRect;
hWE: WEHandle);
procedure WEGetViewRect (var viewRect: LongRect;
hWE: WEHandle);
procedure WESetViewRect (var viewRect: LongRect;
hWE: WEHandle);
function WEGetText (hWE: WEHandle): Handle;
function WEGetChar (offset: LongInt;
hWE: WEHandle): Char;
function WEGetAlignment (hWE: WEHandle): SignedByte;
function WEUseText (text: Handle;
hWE: WEHandle): OSErr;
implementation
uses
FixMath;
procedure WEStopInlineSession (hWE: WEHandle);
var
pWE: WEPtr;
begin
pWE := hWE^;
{ call FixTSMDocument only if the inline input area is actually "open" }
if (pWE^.tsmAreaStart <> kInvalidOffset) then
if (pWE^.tsmReference <> nil) then
if (FixTSMDocument(pWE^.tsmReference) <> noErr) then
;
end; { WEStopInlineSession }
function _WERemoveLine (lineIndex: LongInt;
pWE: WEPtr): OSErr;
{ remove the specified element from the line array }
begin
{ do the removal (errors returned by _WERemoveSlot can be safely ignored) }
_WERemoveLine := _WERemoveSlot(pWE^.hLines, lineIndex, SizeOf(LineRec));
{ decrement line count }
pWE^.nLines := pWE^.nLines - 1;
end; { _WERemoveLine }
function _WEInsertLine (lineIndex: LongInt;
var theLine: LineRec;
pWE: WEPtr): OSErr;
{ insert the specified element in the line array }
var
err: OSErr;
begin
_WEInsertLine := noErr;
{ do the insertion }
err := _WEInsertSlot(pWE^.hLines, @theLine, lineIndex, SizeOf(theLine));
if (err <> noErr) then
begin
_WEInsertLine := err;
Exit(_WEInsertLine);
end;
{ increment line count }
pWE^.nLines := pWE^.nLines + 1;
end; { _WEInsertLine }
procedure _WEBumpOrigin (lineIndex: LongInt;
deltaOrigin: LongInt;
pWE: WEPtr);
var
pOrigin: LongIntPtr;
nLines: LongInt;
begin
pOrigin := @pWE^.hLines^^[lineIndex].lineOrigin;
{ loop through the line run array adjusting the lineOrigin fields }
nLines := pWE^.nLines;
while (lineIndex <= nLines) do
begin
pOrigin^ := pOrigin^ + deltaOrigin;
pOrigin := LongIntPtr(LongInt(pOrigin) + SizeOf(LineRec));
lineIndex := lineIndex + 1;
end;
end; { _WEBumpOrigin }
function _WEFindLineBreak (lineStart: LongInt;
hWE: WEHandle): LongInt;
{ Find where to break the line beginning at lineStart }
{ the WE record and the text must be already locked }
{ the current graphics port must be already set up correctly }
var
pWE: WEPtr;
pText: Ptr;
offset, breakOffset: LongInt;
textLength: LongInt;
remainingLength: LongInt;
segmentStart, segmentEnd: LongInt;
runIndex: LongInt;
runInfo: WERunInfo;
pixelWidth: Fixed;
script, previousScript: ScriptCode;
isBreak: Boolean;
begin
pWE := hWE^;
offset := lineStart;
pText := Ptr(LongInt(pWE^.hText^) + offset);
remainingLength := pWE^.textLength - offset;
{ find the style run index corresponding to the first segment on this line }
runIndex := _WEOffsetToRun(offset, hWE);
{ initialize pixelWidth to the width of the destination rectangle, as a Fixed quantity }
pixelWidth := BSL(pWE^.destRect.right - pWE^.destRect.left, 16);
{ STYLE SEGMENT LOOP }
repeat
{ get style run information for the current style run }
_WEGetIndStyle(runIndex, runInfo, hWE);
runIndex := runIndex + 1;
{ set text attributes in the graphics port }
TextFont(runInfo.runAttrs.runStyle.tsFont);
__TextFace(runInfo.runAttrs.runStyle.tsFace);
TextSize(runInfo.runAttrs.runStyle.tsSize);
{ if we're handling multiscript text, keep track of script boundaries }
if BTST(pWE^.flags, weFNonRoman) then
begin
{ what is the script for this segment? }
script := FontToScript(runInfo.runAttrs.runStyle.tsFont);
{ have we crossed a script run boundary in the middle of a line? }
if (runInfo.runStart > offset) and (script <> previousScript) then
begin
{ leave behind the all previous segments on this line }
offset := runInfo.runStart;
pText := Ptr(LongInt(pWE^.hText^) + offset);
remainingLength := pWE^.textLength - offset;
end;
previousScript := script;
end; { if non-Roman }
{ we'll pass textLength as the second parameter to the line break hook }
{ although this parameter is declared as a long, StyledLineBreak uses only }
{ the low word, so make sure it doesn't trespass the 32,767 byte threshold! }
textLength := _WEPinInRange(remainingLength, 0, maxint);
{ calculate segmentStart and segmentEnd relative to offset }
segmentStart := _WEPinInRange(runInfo.runStart - offset, 0, textLength);
segmentEnd := _WEPinInRange(runInfo.runEnd - offset, 0, textLength);
{ set breakOffset to a non-zero value for the first script run on the line, }
{ set it to zero for all subsequent script runs }
breakOffset := Integer(offset = lineStart);
if (runInfo.runAttrs.runStyle.tsObject <> kNullObject) then
begin
{ EMBEDDED OBJECT }
{ subtract object width from pixelWidth }
pixelWidth := pixelWidth - BSL(WEObjectDescHandle(runInfo.runAttrs.runStyle.tsObject)^^.objectSize.h, 16);
{ stop looping if pixelWidth has gone negative }
isBreak := (pixelWidth < 0);
if (isBreak) then
breakOffset := segmentStart { break line before the object }
else
breakOffset := segmentEnd; { break line after the object }
end
else
begin
{ REGULAR TEXT }
{$IFC WASTE_DEBUG}
_WEAssert(pWE^.lineBreakHook <> nil, 'Missing LineBreak Hook');
{$ENDC}
{ let the line break hook do the work for us }
isBreak := (CallWELineBreakProc(pText, textLength, segmentStart, segmentEnd, pixelWidth, breakOffset, hWE, pWE^.lineBreakHook) <> smBreakOverflow);
end;
{ break the line anyway when we reach the end of the text }
if (segmentEnd >= remainingLength) then
isBreak := true;
until (isBreak);
{ return the offset from lineStart to the break point }
_WEFindLineBreak := (offset - lineStart) + breakOffset;
end; { _WEFindLineBreak }
procedure _WECalcHeights (rangeStart, rangeEnd: LongInt;
var lineAscent, lineDescent: Integer;
hWE: WEHandle);
{ Find the maximum ascent and descent values between rangeStart and rangeEnd }
{ the WE record must be already locked }
{ the current graphics port must be already set up correctly }
var
runIndex: LongInt;
runInfo: WERunInfo;
runAscent, runDescent: Integer;
begin
lineAscent := 1;
lineDescent := 1;
{ find the style run index corresponding to the first segment on this line }
runIndex := _WEOffsetToRun(rangeStart, hWE);
{ STYLE SEGMENT LOOP }
repeat
{ get style run information for the current style run }
_WEGetIndStyle(runIndex, runInfo, hWE);
runIndex := runIndex + 1;
{ calculate ascent and descent (actually, descent + leading) values for this style run }
if (runInfo.runAttrs.runStyle.tsObject <> kNullObject) then
begin
{ EMBEDDED OBJECT }
runAscent := WEObjectDescHandle(runInfo.runAttrs.runStyle.tsObject)^^.objectSize.v;
runDescent := 0;
end
else
begin
{ REGULAR TEXT }
runAscent := runInfo.runAttrs.runAscent;
runDescent := runInfo.runAttrs.runHeight - runAscent;
end;
{ save the maximum values in lineAscent and lineDescent }
if (runAscent > lineAscent) then
lineAscent := runAscent;
if (runDescent > lineDescent) then
lineDescent := runDescent;
{ keep looping until we reach rangeEnd }
until (runInfo.runEnd >= rangeEnd);
end; { _WECalcHeights }
function _WERecalBreaks (var startLine, endLine: LongInt;
hWE: WEHandle): OSErr;
{ Recalculates line breaks, line heights and ascents for all the text or for a portion of it. }
{ On entry, startLine and endLine define a range of lines to recalculate. }
{ On exit, startLine to endLine defines the range of lines actually recalculated }
{ the WE record must already be locked }
label
1;
var
pWE: WEPtr;
pLine: LinePtr;
lineInfo, oldLineInfo: LineRec;
lineIndex: LongInt;
recalThreshold: LongInt;
lineOffset: LongInt;
lineAscent, lineDescent: Integer;
textHeight: LongInt;
saveTextLock: Boolean;
saveEnvironment: QDEnvironment;
err: OSErr;
begin
_WERecalBreaks := noErr;
pWE := hWE^;
{ lock the text }
saveTextLock := _WESetHandleLock(pWE^.hText, true);
{ find the character offset that must be necessarily reached before we can }
{ even consider the possibility of stopping the recalculation process }
{ this offset, recalThreshold, is the last character on endLine _before_ recalculation }
lineIndex := _WEPinInRange(endLine, 0, pWE^.nLines - 1);
recalThreshold := pWE^.hLines^^[lineIndex + 1].lineStart;
{ we start recalculating line breaks from the line actually _preceding_ startLine, }
{ since editing startLine may cause part of its text to fit on the preceding line }
lineIndex := _WEPinInRange(startLine - 1, 0, pWE^.nLines - 1);
{ find where in the text recalculation should begin }
lineInfo := pWE^.hLines^^[lineIndex];
{ save the QuickDraw environment }
_WESaveQDEnvironment(pWE^.port, false, saveEnvironment);
{ MAIN LINE BREAKING LOOP }
repeat
{ find where to break the current line }
lineOffset := _WEFindLineBreak(lineInfo.lineStart, hWE);
{ make sure we advance at least by one character (unless we reached the end of text) }
if (lineOffset <= 0) then
if (lineInfo.lineStart < pWE^.textLength) then
lineOffset := 1;
{ calculate ascent and descent values for this line }
_WECalcHeights(lineInfo.lineStart, lineInfo.lineStart + lineOffset, lineAscent, lineDescent, hWE);
{ save the maximum line ascent for this line in the line array }
pLine := @pWE^.hLines^^[lineIndex];
pLine^.lineAscent := lineAscent;
{ increment counters (go to the next line array entry) }
lineIndex := lineIndex + 1;
lineInfo.lineStart := lineInfo.lineStart + lineOffset;
lineInfo.lineOrigin := lineInfo.lineOrigin + (lineAscent + lineDescent);
pLine := LinePtr(LongInt(pLine) + SizeOf(LineRec));
{ compare the newly calculated line start with the old value }
{ if the new line start comes before the old line start, insert a new element }
oldLineInfo := pLine^;
if (lineIndex > pWE^.nLines) | (lineInfo.lineStart < oldLineInfo.lineStart) then
begin
err := _WEInsertLine(lineIndex, lineInfo, pWE);
{ clean up and exit if we ran out of memory }
if (err <> noErr) then
begin
_WERecalBreaks := err;
goto 1;
end;
end
else
begin
{ overwrite the old element }
pLine^.lineStart := lineInfo.lineStart;
pLine^.lineOrigin := lineInfo.lineOrigin;
{ remove all further elements which have a lineStart field }
{ less than or equal to the current one }
while (lineIndex + 1 <= pWE^.nLines) & (lineInfo.lineStart >= pWE^.hLines^^[lineIndex + 1].lineStart) do
err := _WERemoveLine(lineIndex + 1, pWE);
{ if the new line start is the same as the old one... }
if (lineInfo.lineStart = oldLineInfo.lineStart) then
begin
{ ...and recalThreshold has been reached, we can stop recalculating line breaks }
if (lineInfo.lineStart >= recalThreshold) then
begin
{ although line breaks need not be changed from lineIndex on, }
{ the lineOrigin fields may need to be changed }
if (lineInfo.lineOrigin <> oldLineInfo.lineOrigin) then
_WEBumpOrigin(lineIndex + 1, lineInfo.lineOrigin - oldLineInfo.lineOrigin, pWE);
{ exit from the line breaking loop }
goto 1;
end;
end
else
begin
{ otherwise, the new line start comes after the old line start... }
{ if the current line is the one preceding startLine, warn our caller about this }
if ((lineIndex > 0) and (lineIndex = startLine)) then
startLine := lineIndex - 1;
end;
end;
until (lineInfo.lineStart >= pWE^.textLength);
1:
{ calculate total text height }
textHeight := WEGetHeight(0, pWE^.nLines, hWE);
{ quirk: if the last character in the text is a carriage return, the caret appears }
{ below the last line, so in this case we need to add the extra height to textHeight }
if (WEGetChar(pWE^.textLength - 1, hWE) = CHR(13)) then
textHeight := textHeight + WEGetHeight(pWE^.nLines - 1, pWE^.nLines, hWE);
{ if total text height has changed, remember to call the scroll callback later }
if (textHeight <> pWE^.destRect.bottom - pWE^.destRect.top) then
BSET(pWE^.flags, weFDestRectChanged);
{ set destRect.bottom to destRect.top + total text height }
pWE^.destRect.bottom := pWE^.destRect.top + textHeight;
{ return through endLine the index of the last line affected by recalculation }
endLine := lineIndex - 1;
{ make sure startLine isn't greater than endLine }
if (startLine > endLine) then
startLine := endLine;
{ unlock the text }
if (_WESetHandleLock(pWE^.hText, saveTextLock)) then
;
{ restore the QuickDraw environment }
_WERestoreQDEnvironment(saveEnvironment);
end; { _WERecalBreaks }
procedure _WERecalSlops (firstLine, lastLine: LongInt;
hWE: WEHandle);
{ Calculates the lineSlop and lineJustAmount fields }
{ of the line array for the specified lines }
var
pWE: WEPtr;
textLength: LongInt;
totalSlop, lineWidth: Integer;
totalProportion: Fixed;
function SLCalcSlop (pLine: LinePtr;
pAttrs: WERunAttributesPtr;
pSegment: Ptr;
segmentStart, segmentLength: LongInt;
styleRunPosition: JustStyleCode): Boolean;
var
segmentWidth: Integer;
segmentProportion: Fixed;
isEndOfLine: Boolean;
begin
SLCalcSlop := false; { keep looping }
{ see if this text segment ends with a carriage return, or if we've reached the }
{ end of the text (in which case we don't want any justification to take place) }
isEndOfLine := (segmentStart + segmentLength >= textLength) | (Ptr(LongInt(pSegment) + segmentLength - 1)^ = kEOL);
{ if this is the first segment on the line, reset line totals }
if (styleRunPosition <= leftStyleRun) then
begin
totalSlop := lineWidth;
totalProportion := 0;
end;
if (pAttrs^.runStyle.tsObject <> kNullObject) then
begin
{ EMBEDDED OBJECT }
{ segment width is just object width; no extra space can be applied for justification }
segmentWidth := WEObjectDescHandle(pAttrs^.runStyle.tsObject)^^.objectSize.h;
segmentProportion := 0;
end
else
begin
{ REGULAR TEXT }
{ if this is the last segment on the line, strip trailing spaces }
if (not Odd(styleRunPosition)) then
segmentLength := VisibleLength(pSegment, segmentLength);
{ measure this segment }
segmentWidth := TextWidth(pSegment, 0, segmentLength);
{ calculate the proportion of extra space to apply to this text segment }
segmentProportion := PortionLine(pSegment, segmentLength, styleRunPosition, Point(kOneToOneScaling), Point(kOneToOneScaling));
end;
{ keep track of line totals }
totalSlop := totalSlop - segmentWidth;
totalProportion := totalProportion + segmentProportion;
{ if this is the last segment on the line, save values in the line array }
if (not Odd(styleRunPosition)) then
begin
{ make sure slop is non-negative }
if (totalSlop < 0) then
totalSlop := 0;
pLine^.lineSlop := totalSlop;
if (isEndOfLine) then
pLine^.lineJustAmount := 0
else
pLine^.lineJustAmount := FixDiv(BSL(totalSlop, 16), totalProportion);
end;
end; { SLCalcSlop }
begin
pWE := hWE^;
{ don't bother recalculating slops if the text is left-aligned }
if (pWE^.alignment = weFlushLeft) then
Exit(_WERecalSlops);
textLength := pWE^.textLength;
lineWidth := pWE^.destRect.right - pWE^.destRect.left;
{ calculate slop and normalized slop proportion for all lines }
_WESegmentLoop(firstLine, lastLine, SLCalcSlop, hWE);
end; { _WERecalSlops }
function WEUseText (text: Handle;
hWE: WEHandle): OSErr;
var
pWE: WEPtr;
textLength: LongInt;
saveWELock: Boolean;
begin
WEUseText := noErr;
{ lock the WE record }
saveWELock := _WESetHandleLock(hWE, true);
pWE := hWE^;
{ install the text }
_WEForgetHandle(pWE^.hText);
pWE^.hText := text;
textLength := GetHandleSize(text);
pWE^.textLength := textLength;
pWE^.hRuns^^[pWE^.nRuns].runStart := textLength + 1;
pWE^.hLines^^[pWE^.nLines].lineStart := textLength;
{ unlock the WE record }
if (_WESetHandleLock(hWE, saveWELock)) then
;
end; { WEUseText }
function WEGetAlignment (hWE: WEHandle): SignedByte;
begin
WEGetAlignment := hWE^^.alignment;
end; { WEGetAlignment }
procedure WEGetSelection (var selStart, selEnd: LongInt;
hWE: WEHandle);
var
pWE: WEPtr;
begin
pWE := hWE^;
selStart := pWE^.selStart;
selEnd := pWE^.selEnd;
end; { WEGetSelection }
procedure WESetDestRect (var destRect: LongRect;
hWE: WEHandle);
begin
hWE^^.destRect := destRect;
end; { WESetDestRect }
procedure WEGetDestRect (var destRect: LongRect;
hWE: WEHandle);
begin
destRect := hWE^^.destRect;
end; { WEGetDestRect }
procedure WESetViewRect (var viewRect: LongRect;
hWE: WEHandle);
var
pWE: WEPtr;
r: Rect;
begin
pWE := hWE^;
pWE^.viewRect := viewRect;
{ keep the viewRgn in sync with the view rectangle }
WELongRectToRect(viewRect, r);
RectRgn(pWE^.viewRgn, r);
end; { WESetViewRect }
procedure WEGetViewRect (var viewRect: LongRect;
hWE: WEHandle);
begin
viewRect := hWE^^.viewRect;
end; { WEGetViewRect }
function WEGetTextLength (hWE: WEHandle): LongInt;
begin
WEGetTextLength := hWE^^.textLength;
end; { WEGetTextLength }
function WECountLines (hWE: WEHandle): LongInt;
begin
WECountLines := hWE^^.nLines;
end; { WECountLines }
function WEGetHeight (startLine, endLine: LongInt;
hWE: WEHandle): LongInt;
var
pWE: WEPtr;
pLines: LineArrayPtr;
nLines: LongInt;
begin
pWE := hWE^;
pLines := pWE^.hLines^;
nLines := pWE^.nLines;
startLine := _WEPinInRange(startLine, 0, nLines);
endLine := _WEPinInRange(endLine, 0, nLines);
_WEReorder(startLine, endLine);
WEGetHeight := pLines^[endLine].lineOrigin - pLines^[startLine].lineOrigin;
end; { WEGetHeight }
function WEGetText (hWE: WEHandle): Handle;
begin
WEGetText := hWE^^.hText;
end; { WEGetText }
function WEGetChar (offset: LongInt;
hWE: WEHandle): Char;
var
pWE: WEPtr;
begin
WEGetChar := Char(0);
pWE := hWE^;
{ sanity check: make sure offset is withing allowed bounds }
if ((offset < 0) or (offset >= pWE^.textLength)) then
Exit(WEGetChar);
{ get the specified character (actually, byte) }
WEGetChar := WECharsHandle(pWE^.hText)^^[offset];
end; { WEGetChar }
end.